package org.nutz.dao.impl.sql;

import org.nutz.dao.sql.SqlType;
import org.nutz.dao.sql.VarIndex;
import org.nutz.lang.Strings;

/**
 * @author zozoh
 * @author wendal(wendal1985@gmail.com)
 */
class SqlLiteral implements Cloneable {

	WorkingStack stack;

	private VarIndexImpl varIndexes;

	private VarIndexImpl paramIndexes;

	private String source;

	private SqlType type;

	private void reset() {
		stack = new WorkingStack();
		varIndexes = new VarIndexImpl();
		paramIndexes = new VarIndexImpl();
		// statementIndexes = new VarIndexImpl();
	}

	VarIndex getVarIndexes() {
		return varIndexes;
	}

	VarIndex getParamIndexes() {
		return paramIndexes;
	}

	/**
	 * [@|$][a-zA-Z0-9_-.]+
	 * 
	 * <pre>
	 * 48-57	0-9
	 * 65-90	A-Z
	 * 97-122	a-z
	 * 95		_
	 * 45		-
	 * 46		.
	 * </pre>
	 * 
	 * @param str
	 * @return SqlLiteral
	 */
	SqlLiteral valueOf(String str) {
		reset();
		// int statementIndex = 1;
		source = str;
		if (null == source)
			return this;
		char[] cs = Strings.trim(source).toCharArray();
		StringBuilder sb;
		for (int i = 0; i < cs.length; i++) {
			char c = cs[i];
			switch (c) {
			case '@':
				if (cs[i + 1] == '@') {
					stack.push(c);
					i++;
					break;
				}
				sb = new StringBuilder();
				i = readTokenName(cs, i, sb);
				// Fail to read token name
				if (sb.length() == 0) {
					stack.push(c);
				} else {
					paramIndexes.add(sb.toString(), stack.markToken());
					// paramIndexes.add(name, stack.markToken());
					// statementIndexes.add(name, statementIndex++);
				}
				break;
			case '$':
				if (cs[i + 1] == '$') {
					stack.push(c);
					i++;
					break;
				}
				sb = new StringBuilder();
				i = readTokenName(cs, i, sb);
				// Fail to read token name
				if (sb.length() == 0) {
					stack.push(c);
				} else {
					// varIndexes.add(sb.toString(), stack.markToken());
					varIndexes.add(sb.toString(), stack.markToken());
				}
				break;
			default:
				stack.push(c);
			}
		}
		stack.finish();

		// eval SqlType ...

		if (stack.firstEquals("SELECT") || stack.firstEquals("WITH"))
			type = SqlType.SELECT;
		else if (stack.firstEquals("UPDATE"))
			type = SqlType.UPDATE;
		else if (stack.firstEquals("INSERT"))
			type = SqlType.INSERT;
		else if (stack.firstEquals("DELETE"))
			type = SqlType.DELETE;
		else if (stack.firstEquals("CREATE"))
			type = SqlType.CREATE;
		else if (stack.firstEquals("DROP"))
			type = SqlType.DROP;
		else if (stack.firstEquals("TRUNCATE"))
			type = SqlType.TRUNCATE;
		else if (stack.firstEquals("ALTER"))
			type = SqlType.ALTER;
		else
			type = SqlType.OTHER;

		return this;
	}

	private int readTokenName(char[] cs, int i, StringBuilder sb) {
		for (++i; i < cs.length; i++) {
			int b = (int) cs[i];
			// Special case for underline ('_')
			if (b == 95) {
				sb.append((char) b);
			}
			// 遇到了 '$'
			else if (b == 36) {
				return i;
			}
			// 正常的不可忽略的字符
			else if ((b >= 0 && b <= 47)
						|| (b >= 58 && b <= 64)
						|| (b >= 91 && b <= 96)
						|| (b >= 123 && b <= 160)) {
				break;
			} else {
				sb.append((char) b);
			}
		}
		return i - 1;
	}

	@Override
	public SqlLiteral clone() {
		return new SqlLiteral().valueOf(source);
	}

	public String toString() {
		return source;
	}

	boolean isSELECT() {
		return SqlType.SELECT == type;
	}

	boolean isUPDATE() {
		return SqlType.UPDATE == type;
	}

	boolean isINSERT() {
		return SqlType.INSERT == type;
	}

	boolean isDELETE() {
		return SqlType.DELETE == type;
	}

	boolean isCREATE() {
		return SqlType.CREATE == type;
	}

	boolean isDROP() {
		return SqlType.DROP == type;
	}

	boolean isTRUNCATE() {
		return SqlType.TRUNCATE == type;
	}

	SqlType getType() {
		return type;
	}

}
